package duplicates;

import java.io.*;
import java.util.*;

/**
 * The class implements the DuplicateFinder interface.
 *
 * @author Andrey Murgo
 */
public class DuplicateFinderImpl implements DuplicateFinder {
    @Override
    public boolean process(File sourceFile, File targetFile) {
        try {
            //reading lines from the source file and calculating number of occurrences
            Map<String, Integer> lineMap = findDuplicateInFile(sourceFile, "UTF8");
            //sorting the result
            List<Map.Entry<String, Integer>> lines = new ArrayList<Map.Entry<String, Integer>>(lineMap.entrySet());
            Collections.sort(lines, new Comparator<Map.Entry<String, Integer>>() {
                @Override
                public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
                    return o1.getKey().compareTo(o2.getKey());
                }
            });
            //writing the result in the target file
            printResult(lines, targetFile, "UTF8");
            return true;
        } catch (IOException e) {
            return false;
        }
    }

    /**
     * This function reads lines from the specified file and
     * and puts them into map
     *
     * @param fileName the File to read from
     * @return map containing the unique lines as a keys
     *         and number of their occurrences as a values
     * @throws IOException if an I/O error occurs
     */
    private Map<String, Integer> findDuplicateInFile(File fileName, String charsetName) throws IOException {
        final int assumedLineLength = 50;
        final int mapSize = (int) (fileName.length() / assumedLineLength) * 2;

        Map<String, Integer> resultMap = new HashMap<String, Integer>(mapSize);
        BufferedReader in = new BufferedReader(
                new InputStreamReader(
                        new FileInputStream(fileName),charsetName
                )
        );
        try {
            String s;
            Integer occurNum;
            while ((s = in.readLine()) != null) {
                occurNum = resultMap.get(s);
                resultMap.put(s, occurNum == null ? 1 : occurNum + 1);
            }
        } finally {
            in.close();
        }
        return resultMap;
    }

    /**
     * This function writes lines each followed by number of occurrences in the file
     */
    private void printResult(List<Map.Entry<String, Integer>> lines, File fileName, String charsetName) throws IOException {
         /* Creating directories if it's necessary */
        File parentDirectory = fileName.getParentFile();
        if (parentDirectory != null) {
            parentDirectory.mkdirs();
        }
        PrintWriter out = new PrintWriter(
                new BufferedWriter(
                        new OutputStreamWriter(
                                new FileOutputStream(fileName, true), charsetName
                        )
                )
        );
        try {
            for (Map.Entry<String, Integer> line : lines) {
                out.printf("%s [%d]\n", line.getKey(), line.getValue());
            }
        } finally {
            out.close();
        }
    }
}

